﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Tsanie.Mp3Tag {
    public static class StreamExtension {

        #region - Stream -

        public static int Read(this Stream stream, byte[] buffer, int count) {
            int result = stream.Read(buffer, 0, count);
            if (result < count)
                throw new ArgumentOutOfRangeException("count");
            return result;
        }

        #endregion

        #region - byte[] [Tag] -

        public static string ToTagString(this byte[] buffer) {
            return buffer.ToTagString(null);
        }
        public static string ToTagString(this byte[] buffer, Encoding encoding) {
            if (buffer == null || buffer.Length <= 1)
                return null;
            int last = buffer.Length - 1;
            if (buffer[0] == 0) {   // ISO-8859-1
                if (encoding == null) {
                    encoding = Encoding.Default;
                }
                byte end = buffer[last];
                return encoding.GetString(buffer, 1, (end == 0 ? last - 1 : last));
            } else if (buffer[0] == 1) {    // Unicode
                byte end = buffer[last - 1];
                if (buffer[1] == 0xff || buffer[2] == 0xff)
                    return Encoding.Unicode.GetString(buffer, 3, (end == 0 ? last - 4 : last - 2));
                return Encoding.Unicode.GetString(buffer, 1, (end == 0 ? last - 2 : last));
            } else if (buffer[0] == 3) {    // UTF-8
                byte end = buffer[last];
                return Encoding.UTF8.GetString(buffer, 1, (end == 0 ? last - 1 : last));
            }
            return Encoding.Default.GetString(buffer, 1, last);
        }
        public static int ToTagInt(this byte[] buffer) {
            return ((buffer[0] & 0x7f) << 21) |
                   ((buffer[1] & 0x7f) << 14) |
                   ((buffer[2] & 0x7f) << 7) |
                   ((buffer[3] & 0x7f));
        }

        #endregion

        public static int ToInt(this byte[] buffer) {
            return (buffer[0] << 24) |
                   (buffer[1] << 16) |
                   (buffer[2] << 8) |
                   (buffer[3]);
        }
        public static short ToShort(this byte[] buffer) {
            return (short)((buffer[0] << 8) | buffer[1]);
        }
        public static string ToID(this byte[] buffer) {
            StringBuilder sb = new StringBuilder(0x40);
            for (int i = 0; i < buffer.Length; i++)
                sb.Append((char)buffer[i]);
            return sb.ToString();
        }

        public static byte[] ToTagIntBytes(this int value) {
            byte[] buffer = new byte[4];
            for (int i = 3; i >= 0; i--) {
                buffer[i] = (byte)(value & 0x7f);
                value >>= 7;
            }
            return buffer;
        }
        public static byte[] ToIntBytes(this int value) {
            byte[] buffer = new byte[4];
            for (int i = 3; i >= 0; i--) {
                buffer[i] = (byte)value;
                value >>= 8;
            }
            return buffer;
        }

        public static byte[] ToTagBytes(this string str) {
            return str.ToTagBytes(null);
        }
        public static byte[] ToTagBytes(this string str, Encoding encoding) {
            if (encoding == null)
                encoding = Encoding.Unicode;
            bool unicode = (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode);
            byte[] bs = encoding.GetBytes(str);
            byte[] result = new byte[bs.Length + (unicode ? 5 : 2)];
            if (unicode) {
                result[0] = 1;
                if (encoding == Encoding.Unicode) {
                    result[1] = 0xff;
                    result[2] = 0xfe;
                } else {
                    result[1] = 0xfe;
                    result[2] = 0xff;
                }
                result[result.Length - 2] = 0;
                result[result.Length - 1] = 0;
                Array.Copy(bs, 0, result, 3, bs.Length);
                return result;
            } else if (encoding == Encoding.UTF8) {
                result[0] = 3;
            } else {
                result[0] = 0;
            }
            result[result.Length - 1] = 0;
            Array.Copy(bs, 0, result, 1, bs.Length);
            return result;
        }
    }
}
